package br.com.ps.bombertournament.game;

import java.util.HashMap;
import java.util.Map;

import br.com.ps.bombertournament.game.animation.Action;
import br.com.ps.bombertournament.game.animation.AnimationGameObject;
import br.com.ps.bombertournament.game.animation.Frame;
import br.com.ps.bombertournament.game.math.Rectangle;

import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

/**
 * Base GameObject class.
 * <br>Contains all properties what the Game Object is necessary (animations, volume, id, layer).
 * 
 * @author Rennan Stefan Boni - rennanboni@gmail.com
 */
public abstract class BaseGameObject extends Sprite implements GameObject, Cloneable {

	/* Indicate the transparency */
	protected float transparency = 1;
	/* Current animation on execution */
	protected AnimationGameObject currentAnimation;
	/* All animations available */
	protected Map<String, AnimationGameObject> animations;

	protected boolean touchable;
	/* The volume for the collision engine */
	protected Rectangle volume;
	
	/* The identification of object */
	protected String id;
	
	/* The layer the object belongs */
	protected Layer layer;
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.ps.bombertournament.game.object.GameObject#update(float)
	 */
	public void update(float deltaTime) {
		if (currentAnimation != null) {
			Frame frame = currentAnimation.getKeyFrame(deltaTime);
			
			if (frame != null) {
				executeActions(frame);
				
				if (frame.getTexture() != null) {
					setCurrentTexture(frame.getTexture());
				}
			}
		}
	}
	
	/**
	 * Execute all actions presents in the {@link Frame}
	 * 
	 * @param frame The {@link Frame} that will be analyse
	 */
	private void executeActions(Frame frame) {
		if (frame != null && frame.getActions() != null) {
			for (Action action : frame.getActions()) {
				if (action.isExecuted() == false) {
					action.execute(this);
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.ps.bombertournament.game.object.GameObject#render(com.badlogic.gdx.graphics.g2d.SpriteBatch)
	 */
	public void render(SpriteBatch spriteBatch) {
		if (super.getTexture() != null) {
			super.draw(spriteBatch, transparency);
		}
	}
	
	/**
	 * Dispose the game object
	 */
	public void dispose() {
		if (animations != null) {
			for (String key : animations.keySet()) {
				animations.get(key).dispose();
			}
			
			animations.clear();
		}
	}
	
	/**
	 * Play the animation with name in <code>animation</code>.
	 * 
	 * @param animation Name animation will be played
	 * @return Return true if the animation was found and prepared, other case return false 
	 */
	public boolean play(String animation) {
		boolean result = this.animations.containsKey(animation);
		
		if (result) {
			this.currentAnimation = this.animations.get(animation);
			this.currentAnimation.reset();
		}
		
		return result;
	}
	
	/**
	 * Analyze if the other volume's game object intercept the local volume game object.
	 * 
	 * @param other The other game object to be analyzed
	 * @return Return true if the local object volumes intercepts the other volumes's object
	 */
	public boolean instercept(BaseGameObject other) {
		if (this.getVolume() == null || other.getVolume() == null) {
			return false;
		}
		
		return this.getVolume().intercept(other.getVolume());
	}
	
	public boolean instercept(int x, int y) {
		return this.getVolume().intercept(new Rectangle(x, y, x, y));
	}
	
	/**
	 * Stop the current animation
	 */
	public void stop() {
		if (currentAnimation != null) {
			currentAnimation = null;
		}
	}

	public void setAnimations(Map<String, AnimationGameObject> animations) {
		this.animations = animations;
	}

	public boolean isTouchable() {
		return touchable;
	}

	public void setTouchable(boolean isTouchable) {
		this.touchable = isTouchable;
	}

	public void setCurrentTexture(TextureRegion currentTexture) {
		super.setRegion(currentTexture);
		super.setTexture(currentTexture.getTexture());
	}

	public Layer getLayer() {
		return layer;
	}

	public void setLayer(Layer layer) {
		this.layer = layer;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
	
	public void setTransparency(Float transparency) {
		this.transparency = transparency;
	}
	
	/**
	 * Return the volume of the object with the current X and Y points
	 * @return Return a {@link Rectangle} object.
	 */
	public Rectangle getVolume() {
		if (volume != null) {
			volume.x = super.getX();
			volume.y = super.getY();
			return volume;
		}
		
		return null;
	}
	
	@Override
	public void setScale(float scaleX, float scaleY) {
		super.setScale(scaleX, scaleY);
		
		if (volume != null) {
			volume.width = volume.width * scaleX;
			volume.height = volume.height * scaleY;
		}
	}

	@Override
	public void setScale(float scaleXY) {
		this.setScale(scaleXY, scaleXY);
	}

	public void setVolume(Rectangle volume) {
		this.volume = volume;
	}
	
	@Override
	public BaseGameObject clone() {
		try {
			BaseGameObject result = (BaseGameObject) super.clone();
			
			if (result.animations != null) {
				Map<String, AnimationGameObject> animationsClone = new HashMap<String, AnimationGameObject>(result.animations.size());
				for (String key : result.animations.keySet()) {
					animationsClone.put(key, result.animations.get(key).clone());
				}
				result.animations = animationsClone;
			}
			
			if (result.currentAnimation != null) {
				result.currentAnimation = result.currentAnimation.clone();
			}
			
			if (result.volume != null) {
				result.volume = result.volume.clone();
			}
			
			if (result.vertices != null) {
				result.vertices = result.vertices.clone();
			}
			
			return result;
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * Indicate if game object was remove from the scene
	 * 
	 * @return Return true if the object was removed, otherwise return false
	 */
	public boolean isOnScene() {
		return layer != null;
	}

	/**
	 * Called when the object is removed from the layer.
	 * That mean the object will be not rendered on the scene
	 */
	public abstract void onRemove();

	/**
	 * Called when the object is intercepted with other object on the scene
	 * @param other Another object.
	 */
	public abstract void onCollision(BaseGameObject other);
}
